package com.sanyou.service.impl;

import cn.hutool.core.convert.Convert;
import com.alibaba.fastjson.JSON;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.common.collect.Lists;
import com.sanyou.builders.CacheKeyBuilder;
import com.sanyou.cache.AbstractRedisCacheService;
import com.sanyou.cache.HashCacheService;
import com.sanyou.cache.ZSetCacheService;
import com.sanyou.enums.IsOk;
import com.sanyou.enums.MaintainType;
import com.sanyou.enums.ModuleName;
import com.sanyou.mapper.CopperParticleMapper;
import com.sanyou.mapper.OperLogMapper;
import com.sanyou.mapper.ProductMapper;
import com.sanyou.pojo.CopperParticleCheckRecord;
import com.sanyou.pojo.Equipment;
import com.sanyou.pojo.OperLog;
import com.sanyou.pojo.Product;
import com.sanyou.pojo.exceptions.CacheException;
import com.sanyou.pojo.vo.CopperParticleHistoryVo;
import com.sanyou.pojo.vo.CopperParticleReqVo;
import com.sanyou.pojo.vo.CopperParticleResVo;
import com.sanyou.pojo.vo.IndustryDataVo;
import com.sanyou.service.CacheService;
import com.sanyou.service.CopperParticleService;
import com.sanyou.service.EquipmentService;
import com.sanyou.service.OperLogService;
import com.sanyou.service.ProductService;
import com.sanyou.utils.*;
import org.apache.ibatis.session.RowBounds;
import org.apache.poi.ss.formula.functions.T;
import org.checkerframework.checker.units.qual.A;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import tk.mybatis.mapper.entity.Example;

import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import com.sanyou.cache.AbstractRedisCacheService.RedisType;

import javax.validation.constraints.NotNull;

/**
 * 铜粒子相关功能模块
 * @Author xhy
 * @Date 2022/11/2
 */
@Service
public class CopperParticleServiceImpl implements CopperParticleService {

    @Autowired
    private HashCacheService hashCacheService;

    @Autowired
    private ZSetCacheService zSetCacheService;

    @Autowired
    private CopperParticleMapper copperParticleMapper;

    @Autowired
    private ProductService productService;

    @Autowired
    private OperLogService operLogService;

    @Autowired
    private EquipmentService equipmentService;

    /**
     * 插入铜粒子检测记录
     * @param record
     */
    //@Transactional(propagation = Propagation.REQUIRED)
    @Override
    public void insertRecord(CopperParticleCheckRecord record) throws Exception {
        /**
         * 添加记录id
         */
        if(record.getId()==null||record.getId()==0){
            record.setId(SnowFlake.getNextId());
        }
        record.setCreatetime(new Date());//更新记录上传时间
//        /**
//         * 若是当天的数据则插入到缓存数据库中，否则持久化
//         */
//        if(TimeUtils.isToday(record.getDatetime())){
//            this.insertRecordToCache(record);
//        }else{
//            copperParticleMapper.insert(record);
//        }

        /**
         * 持久化数据
         */
        copperParticleMapper.insert(record);

        /**
         * 生成时间线日志(铜粒子日志)
         */
        operLogService.insert(record.getProductCode(),
                2,
                0,
                "A面:"+record.getaIsOk()+",B面:"+record.getbIsOk(),
                "system");


        //如果当前阴极板状态为备用,则更新为在线状态
        Product product = productService.getProductByCode(record.getProductCode());
        if(product != null && product.getProductState() == MaintainType.Reserve.getCode()){
            /**
             * 将对应阴极板的状态变更为在线
             */
            product.setProductState(MaintainType.Using.getCode());
            product.setUpdateStateDate(new Date());
            product.setUserid("system");
            productService.updateProduct(product);
        }

    }

    @Override
    public void insertRecords(List<CopperParticleCheckRecord> records) throws Exception {
        for (CopperParticleCheckRecord record:records) {
            this.insertRecord(record);
        }
    }

    /**
     * 插入数据到缓存数据库中（更新版）
     * @param record
     * @throws Exception
     */
    @Override
    public void insertRecordToCache(CopperParticleCheckRecord record) throws Exception {
        //Hash表的key
        String hashKey = generateDefaultCacheKey(new Date(),RedisType.HASH);
        //Hash表的Field
        String field = generateDefaultHashField(record.getId().toString(),record.getDeviceNo(),recordIsOk(record)?IsOk.OK:IsOk.NG,record.getProductCode(),record.getType());
        //Zset表的key
        //String zsetKey = generateDefaultCacheKey(new Date(),record.getDeviceNo(),RedisType.ZSET);
        //插入Hash缓存表中
        hashCacheService.hSet(hashKey,field,record);
        //插入常驻顺序表
        //zSetCacheService.zAdd(zsetKey,field,record.getDatetime().getTime());
        //插入常驻索引表
        this.addIndexOfProductNo(record.getProductCode(),field);
        //插入临时表
        this.insertToTempleTables(record);

        //按小时进行计数统计
        String statKey = new CacheKeyBuilder(generateDefaultCacheKey(new Date(),RedisType.STAT))
                .addSecondaryKey(record.getDeviceNo())
                .addSecondaryKey(new SimpleDateFormat("HH").format(record.getDatetime()))
                .build();
        ((AbstractRedisCacheService)hashCacheService).incr(statKey);
    }

    /**
     * 获取缓存中目标设备所产生的所有铜粒子检测记录（更新版）
     * 缓存中只存在当天的数据
     * @param deviceNo
     * @return
     */
    @Override
    public List<CopperParticleCheckRecord> getAllRecordsFromCache(String deviceNo) {
        try {
            return getAllRecordsFromCache(deviceNo,null,"","");
        }catch (Exception e){
            return null;
        }
    }

    /**
     *
     * @param deviceNo 设备编号(非空，精确匹配)，空字符串时表示匹配所有设备
     * @param isOk null代表同时搜索合格与不合格的记录；OK搜索合格记录；NG搜索不合格记录
     * @param productNo 产品编号(非空，模糊匹配)，空字符串时表示匹配所有阴极板编号
     * @param particleType 例子类型(非空，精确匹配)，空字符串时表示匹配所有阴极板编号
     * @return
     * @throws CacheException
     */
    @Override
    public List<CopperParticleCheckRecord> getAllRecordsFromCache(@NotNull String deviceNo, IsOk isOk, @NotNull String productNo,@NotNull String particleType) throws CacheException {
        //构建条件匹配串
        String option = generateDefaultHashField("",deviceNo,isOk,productNo+"*",particleType);
        //构建临时表名
        String tempKey = new CacheKeyBuilder(generateDefaultCacheKey(new Date(),RedisType.TEMP))
                .addSecondaryKey(option)
                .build();
        //如果存在对应的临时表则返回该临时表数据,否则建立新的临时表
        if(zSetCacheService.isExist(tempKey)){
            Set<String> fields = zSetCacheService.zRangeAll(tempKey,String.class);
            zSetCacheService.setExpire(tempKey);//更新过期时间
            return this.getRecordsByFields(fields);
        }else{
            /**
             * 根据参数条件搜索记录，并建立新的临时表
             */
            //筛选记录
            Map<String,String> map = hashCacheService.hScan(generateDefaultCacheKey(new Date(),RedisType.HASH),option);
            List<CopperParticleCheckRecord> result = new ArrayList<>();
            CopperParticleCheckRecord recordTemp = null;
            for (String field:map.keySet()){
                recordTemp = hashCacheService.hGet(generateDefaultCacheKey(new Date(),RedisType.HASH),field,CopperParticleCheckRecord.class);
                result.add(recordTemp);//根据符合条件的field获取相应的记录对象
                zSetCacheService.zAdd(tempKey,field,recordTemp.getDatetime().getTime());//建立临时表
            }
            zSetCacheService.setExpire(tempKey);//设置临时表过期时间
            return result;
        }
    }

    public void generateTempTable(@NotNull String deviceNo, IsOk isOk, @NotNull String productNo,@NotNull String particleType) throws CacheException {
        //构建条件匹配串
        String option = generateDefaultHashField("",deviceNo,isOk,productNo+"*",particleType);
        //构建临时表名
        String tempKey = new CacheKeyBuilder(generateDefaultCacheKey(new Date(),RedisType.TEMP))
                .addSecondaryKey(option)
                .build();
        //如果存在对应的临时表则返回该临时表数据,否则建立新的临时表
        if(!zSetCacheService.isExist(tempKey)){
            /**
             * 根据参数条件搜索记录，并建立新的临时表
             */
            //筛选记录
            Map<String,String> map = hashCacheService.hScan(generateDefaultCacheKey(new Date(),RedisType.HASH),option);
            List<CopperParticleCheckRecord> result = new ArrayList<>();
            CopperParticleCheckRecord recordTemp = null;
            for (String field:map.keySet()){
                recordTemp = hashCacheService.hGet(generateDefaultCacheKey(new Date(),RedisType.HASH),field,CopperParticleCheckRecord.class);
                zSetCacheService.zAdd(tempKey,field,recordTemp.getDatetime().getTime());//建立临时表
            }
            zSetCacheService.setExpire(tempKey);//设置临时表过期时间
        }
    }
    @Override
    public long getAllRecordsFromCacheLength( String deviceNo, IsOk isOk,String productNo,String particleType) throws  Exception{
        generateTempTable(deviceNo,isOk,productNo,particleType);
        //构建条件匹配串
        String option = generateDefaultHashField("",deviceNo,isOk,productNo+"*",particleType);
        //构建临时表名
        String tempKey = new CacheKeyBuilder(generateDefaultCacheKey(new Date(),RedisType.TEMP))
                .addSecondaryKey(option)
                .build();
        return zSetCacheService.getSize(tempKey);
        //return getAllRecordsFromCache(deviceNo,isOk,productNo,particleType).size();
    }
    @Override
    public PagedResult getAllRecordsFromCache(String deviceNo, IsOk isOk, String productNo,String particleType,int page,int pageSize)throws CacheException{
        long before = System.currentTimeMillis();
        //创建生成临时表
        //getAllRecordsFromCache(deviceNo,isOk,productNo,particleType);
        generateTempTable(deviceNo,isOk,productNo,particleType);
        List<CopperParticleCheckRecord> list=new ArrayList<>();
        //构建临时表对应的tempkey
        PagedResult result=new PagedResult();
        String tempkey = new CacheKeyBuilder(generateDefaultCacheKey(new Date(),RedisType.TEMP))
                .addSecondaryKey(generateDefaultHashField("",deviceNo,isOk,productNo,particleType))
                .build();
        //分页
        long total=zSetCacheService.getSize(tempkey);
        if(total<page*pageSize-1){
            Set<String> fields =zSetCacheService.zRevrange(tempkey,String.class,(page-1)*pageSize,-1);
            list=this.getRecordsByFields(fields);
        }
        else{
            Set<String> fields =zSetCacheService.zRevrange(tempkey,String.class,(page-1)*pageSize,page*pageSize-1);
            list=this.getRecordsByFields(fields);
        }
        int x=(int)(total%pageSize>0?total/pageSize+1:total/pageSize);
        //把内容全部塞进去
        result.setRows(list);
        result.setPage(page);
        result.setTotal(x);
        result.setRecords(total);
        return  result;
    }

    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public CopperParticleResVo getList(CopperParticleReqVo copperParticleReqVo, Integer size, Integer page) throws Exception {
        List<CopperParticleCheckRecord> copperParticleList;
        Long pageCount = 0L;
        String deviceNo = copperParticleReqVo.getDeviceNo();
        LocalDate startTime = this.date2LocalDate(copperParticleReqVo.getStartTime());
//        if (startTime.isEqual(LocalDate.now())) {
//            copperParticleList = (List<CopperParticleCheckRecord>) this.getAllRecordsFromCache(copperParticleReqVo.getDeviceNo(), null, "", "", page, size).getRows();
//        } else {
//            Example example = new Example(CopperParticleCheckRecord.class);
//            example.createCriteria()
//                    .andBetween(BeanUtils.convertToFieldName(CopperParticleCheckRecord::getDatetime), startTime, this.localDateTime2Date(startTime.atTime(LocalTime.MAX)))
//                    .andEqualTo(BeanUtils.convertToFieldName(CopperParticleCheckRecord::getDeviceNo), deviceNo);
//            // 总页数
//            // pageCount = (long) (NumberUtil.div(1.0*copperParticleMapper.selectCountByExample(example), 1.0*size, 0, RoundingMode.UP));
//            // 分页
//            RowBounds rowBounds = new RowBounds((page-1)*size, size);
//            copperParticleList = copperParticleMapper.selectByExampleAndRowBounds(example, rowBounds);
//        }
        Example example = new Example(CopperParticleCheckRecord.class);
        example.createCriteria()
                .andBetween(BeanUtils.convertToFieldName(CopperParticleCheckRecord::getDatetime), startTime, this.localDateTime2Date(startTime.atTime(LocalTime.MAX)))
                .andEqualTo(BeanUtils.convertToFieldName(CopperParticleCheckRecord::getDeviceNo), deviceNo);
        // 总页数
        // pageCount = (long) (NumberUtil.div(1.0*copperParticleMapper.selectCountByExample(example), 1.0*size, 0, RoundingMode.UP));
        // 分页
        RowBounds rowBounds = new RowBounds((page-1)*size, size);
        copperParticleList = copperParticleMapper.selectByExampleAndRowBounds(example, rowBounds);
        Long unqualifiedCopperNum = this.getUnqualifiedCopperNum(deviceNo, startTime);
        Long qualifiedCopperNum = this.getQualifiedCopperNum(deviceNo, startTime);
        return new CopperParticleResVo(
                qualifiedCopperNum + unqualifiedCopperNum,
                unqualifiedCopperNum,
                qualifiedCopperNum,
                copperParticleList,
                pageCount
        );
    }

    @Override
    public Long getUnqualifiedCopperNum(String deviceNo, LocalDate startTime) throws Exception {
        Long detectNum;
//        if (startTime.isEqual(LocalDate.now())) {
//            detectNum = this.getAllRecordsFromCacheLength(deviceNo, IsOk.NG);
//        } else {
//            Example example = new Example(CopperParticleCheckRecord.class);
//            example.createCriteria()
//                    .andBetween(BeanUtils.convertToFieldName(CopperParticleCheckRecord::getDatetime), startTime, this.localDateTime2Date(startTime.atTime(LocalTime.MAX)))
//                    .andEqualTo(BeanUtils.convertToFieldName(CopperParticleCheckRecord::getDeviceNo), deviceNo)
//                    .andCondition("(a_isOk = 'NG' or b_isOk = 'NG')");
//            detectNum = Convert.toLong(copperParticleMapper.selectCountByExample(example));
//        }
        Example example = new Example(CopperParticleCheckRecord.class);
        example.createCriteria()
                .andBetween(BeanUtils.convertToFieldName(CopperParticleCheckRecord::getDatetime), startTime, this.localDateTime2Date(startTime.atTime(LocalTime.MAX)))
                .andEqualTo(BeanUtils.convertToFieldName(CopperParticleCheckRecord::getDeviceNo), deviceNo)
                .andCondition("(a_isOk = 'NG' or b_isOk = 'NG')");
        detectNum = Convert.toLong(copperParticleMapper.selectCountByExample(example));
        return detectNum;
    }

    @Override
    public Long getQualifiedCopperNum(String deviceNo, LocalDate startTime) throws Exception {
        Long detectNum;
//        if (startTime.isEqual(LocalDate.now())) {
//            detectNum = this.getAllRecordsFromCacheLength(deviceNo, IsOk.OK);
//        } else {
//            Example example = new Example(CopperParticleCheckRecord.class);
//            example.createCriteria()
//                    .andBetween(BeanUtils.convertToFieldName(CopperParticleCheckRecord::getDatetime), startTime, this.localDateTime2Date(startTime.atTime(LocalTime.MAX)))
//                    .andEqualTo(BeanUtils.convertToFieldName(CopperParticleCheckRecord::getDeviceNo), deviceNo)
//                    .andEqualTo(BeanUtils.convertToFieldName(CopperParticleCheckRecord::getbIsOk), IsOk.OK.getCode())
//                    .andEqualTo(BeanUtils.convertToFieldName(CopperParticleCheckRecord::getaIsOk), IsOk.OK.getCode());
//            detectNum = Convert.toLong(copperParticleMapper.selectCountByExample(example));
//        }
        Example example = new Example(CopperParticleCheckRecord.class);
        example.createCriteria()
                .andBetween(BeanUtils.convertToFieldName(CopperParticleCheckRecord::getDatetime), startTime, this.localDateTime2Date(startTime.atTime(LocalTime.MAX)))
                .andEqualTo(BeanUtils.convertToFieldName(CopperParticleCheckRecord::getDeviceNo), deviceNo)
                .andEqualTo(BeanUtils.convertToFieldName(CopperParticleCheckRecord::getbIsOk), IsOk.OK.getCode())
                .andEqualTo(BeanUtils.convertToFieldName(CopperParticleCheckRecord::getaIsOk), IsOk.OK.getCode());
        detectNum = Convert.toLong(copperParticleMapper.selectCountByExample(example));
        return detectNum;
    }

    @Override
    public LocalDate date2LocalDate(Date date) {
        return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
    }
/**
 * @description: 获取当日检测次数按时间分布
 * @param deviceNo
 * @return: java.util.Map<java.lang.Integer,java.lang.Integer>
 * @author: chase
 * @time: 2022/11/17 下午4:08
 */
    @Override
    public Map<Integer,Integer> getRtTimeDistribution(String deviceNo) {
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.HOUR_OF_DAY,0);
        HashMap<Integer, Integer> map = new HashMap<Integer,Integer>();
        String statKey = generateDefaultCacheKey(new Date(),RedisType.STAT);
        Object num=null;
        //map初始化
        for (int i = 0; i < 24 ; i++) {
            num = ((AbstractRedisCacheService)hashCacheService).get(statKey+":"+deviceNo+":"+new SimpleDateFormat("HH").format(calendar.getTime()));
            if(num==null){
                map.put(i,0);
            }else {
                map.put(i,Integer.valueOf(num.toString()));
            }
            calendar.add(Calendar.HOUR_OF_DAY,1);

        }
        /*List<CopperParticleCheckRecord> records = getAllRecordsFromCache(deviceNo);
        HashMap<Integer, Integer> map = new HashMap<Integer,Integer>();
        //map初始化
        for (int i = 1; i <= 24 ; i++) {
            map.put(i,0);
        }


        for ( CopperParticleCheckRecord record:records)
        {
            int recordTime = record.getDatetime().getHours();
           if (map.get(recordTime)!=null){
               map.put(recordTime,map.get(recordTime)+1);
           }

        }*/

        return map;
    }

    /**
     * @description: 获取某日各小时检测数据
     * @param deviceNo
     * @param startTime
     * @return: java.util.Map<java.lang.Integer,java.lang.Integer>
     * @author: chase
     * @time: 2022/11/16 下午10:13
     */
    @Override
    public Map<Integer, Integer> getHisTimeDistribution(String deviceNo,Date startTime) {
        HashMap<Integer, Integer> map = new HashMap<Integer,Integer>();
        //List<CopperParticleCheckRecord> recordList = getDateTimeByNo(deviceNo);
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(startTime);
        calendar.set(Calendar.HOUR_OF_DAY,0);
        calendar.set(Calendar.MINUTE,0);
        calendar.set(Calendar.SECOND,0);
        Date start = calendar.getTime();
        calendar.add(Calendar.DAY_OF_MONTH,1);
        Date end = calendar.getTime();
        List<CopperParticleCheckRecord> timeList = getCopperParticleRecords(deviceNo,start,end,null,null,null);

        //筛选符合日期的记录
//        List<CopperParticleCheckRecord> timeList = new ArrayList<>();
//        for (CopperParticleCheckRecord record:recordList
//        ) {
//
//            Boolean isameDay = new TimeUtils().isSameDay(startTime, record.getDatetime());
//
//            if(isameDay){ timeList.add(record);}
//        }

        //map初始化
        for (int i = 1; i <= 24 ; i++) {
            map.put(i,0);
        }
        for ( CopperParticleCheckRecord record:timeList)
        {
            int recordTime = record.getDatetime().getHours();
            if (map.get(recordTime)!=null){
                map.put(recordTime,map.get(recordTime)+1);
            }

        }

        return map;
    }

    /**
     * @description: 查找对应设备的所有检测时间
     * @param deviceNo
     * @return: java.util.List<com.sanyou.pojo.CopperParticleCheckRecord>
     * @author: chase
     * @time: 2022/11/16 下午10:05
     */
    private List<CopperParticleCheckRecord> getDateTimeByNo(String deviceNo ) {
        Example example = new Example(CopperParticleCheckRecord.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("deviceNo",deviceNo);
        List<CopperParticleCheckRecord> recordList = copperParticleMapper.selectByExample(example);

        return recordList;

    }

    private Date localDateTime2Date(LocalDateTime localDateTime) {
        return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
    }

    //subList手动分页，page为第几页，rows为每页个数
    public List<T> subList(List<T> list, int page, int rows) {
        List<T> listSort  = new ArrayList<>();
        int size=list.size();
        int pageStart=page==1?0:(page-1)*rows;//截取的开始位置
        int pageEnd=size<page*rows?size:page*rows;//截取的结束位置
        if(size>pageStart){
            listSort =list.subList(pageStart, pageEnd);
        }
        return listSort;
    }

    /**
     *  获取铜粒子历史数据
     */
    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public List<CopperParticleHistoryVo> getHistoryList(String subFactoryNo, Date startDate, Date endDate, Integer interval){
        ArrayList<CopperParticleHistoryVo> list = new ArrayList<>();

        //根据车间号获取所有的设备号
        List<String> deviceNo = equipmentService.getEquipBySubFactoryId(subFactoryNo)
                .stream().map(Equipment::getEquipNo).collect(Collectors.toList());
        //获取开始到结束之间的日期
        List<LocalDate> dateList = DateUtils.getBetweenDate(this.date2LocalDate(startDate), this.date2LocalDate(endDate));
        //TODO 车间没设备临时处理 否则 andIn 会报错
        if (deviceNo.size() == 0){
            dateList.forEach(d -> list.add(new CopperParticleHistoryVo(d,0L, 0L)));
            return toIntervalList(list, interval);
        }
        //车间有设备
        dateList.forEach(d -> list.add(new CopperParticleHistoryVo(d,
                this.getUnqualifiedNum(deviceNo, d),
                this.getQualifiedNum(deviceNo, d)
                )));
        return toIntervalList(list, interval);
    }

    /**
     * 转换为周期统计的历史数据
     * @param list
     * @param interval 统计周期，为 1 直接返回
     * @return
     */
    private List<CopperParticleHistoryVo> toIntervalList(List<CopperParticleHistoryVo> list, Integer interval){
        if (interval == 1)
            return list;

        //按统计周期切割
        List<List<CopperParticleHistoryVo>> partition = Lists.partition(list, interval);
        List<CopperParticleHistoryVo> result = new ArrayList<CopperParticleHistoryVo>();
        //加和
        for (List<CopperParticleHistoryVo> temp : partition) {
            CopperParticleHistoryVo vo = new CopperParticleHistoryVo();
            for (int j = 0; j < temp.size(); j++) {
                if (j == 0 && j == temp.size() - 1) {
                    vo.setStart(temp.get(j).getDate());
                    vo.setEnd(temp.get(j).getDate());
                    vo.setQualifiedCopperNum(temp.get(j).getQualifiedCopperNum());
                    vo.setUnqualifiedCopperNum(temp.get(j).getUnqualifiedCopperNum());
                    result.add(vo);
                }else if (j == 0){
                    vo.setStart(temp.get(j).getDate());
                    vo.setQualifiedCopperNum(temp.get(j).getQualifiedCopperNum());
                    vo.setUnqualifiedCopperNum(temp.get(j).getUnqualifiedCopperNum());
                } else if (j == (interval - 1) || j == temp.size() - 1) {
                    vo.setEnd(temp.get(j).getDate());
                    vo.setQualifiedCopperNum(vo.getQualifiedCopperNum() + temp.get(j).getQualifiedCopperNum());
                    vo.setUnqualifiedCopperNum(vo.getUnqualifiedCopperNum() + temp.get(j).getUnqualifiedCopperNum());
                    result.add(vo);
                } else {
                    vo.setQualifiedCopperNum(vo.getQualifiedCopperNum() + temp.get(j).getQualifiedCopperNum());
                    vo.setUnqualifiedCopperNum(vo.getUnqualifiedCopperNum() + temp.get(j).getUnqualifiedCopperNum());
                }
            }
        }
        return result;
    }

    /**
     * 获取合格的铜粒子数目（日期只能在当天之前）
     * @param deviceNo 所有要查询的设备号
     * @param date 日期
     * @return
     */
    private Long getQualifiedNum(List<String> deviceNo, LocalDate date){
        Example example = new Example(CopperParticleCheckRecord.class);
        example.createCriteria()
                .andBetween(BeanUtils.convertToFieldName(CopperParticleCheckRecord::getDatetime), date, this.localDateTime2Date(date.atTime(LocalTime.MAX)))
                .andIn(BeanUtils.convertToFieldName(CopperParticleCheckRecord::getDeviceNo), deviceNo)
                .andEqualTo(BeanUtils.convertToFieldName(CopperParticleCheckRecord::getbIsOk), IsOk.OK.getCode())
                .andEqualTo(BeanUtils.convertToFieldName(CopperParticleCheckRecord::getaIsOk), IsOk.OK.getCode());
         return Convert.toLong(copperParticleMapper.selectCountByExample(example));
    }

    /**
     * 获取不合格的铜粒子数目（日期只能在当天之前）
     * @param deviceNo 所有要查询的设备号
     * @param date 日期
     * @return
     */
    private Long getUnqualifiedNum(List<String> deviceNo, LocalDate date){
        Example example = new Example(CopperParticleCheckRecord.class);
        example.createCriteria()
                .andBetween(BeanUtils.convertToFieldName(CopperParticleCheckRecord::getDatetime), date, this.localDateTime2Date(date.atTime(LocalTime.MAX)))
                .andIn(BeanUtils.convertToFieldName(CopperParticleCheckRecord::getDeviceNo), deviceNo)
                .andCondition("(a_isOk = 'NG' or b_isOk = 'NG')");
        return Convert.toLong(copperParticleMapper.selectCountByExample(example));
    }


    /**
     * 获取每条铜粒子历史数据
     */
    @Override
    public PagedResult listHistoryRecord(String subFactoryNo, Date startDate, Date endDate, Integer size, Integer page) {
        //根据车间号获取所有的设备号
        List<String> deviceNo = equipmentService.getEquipBySubFactoryId(subFactoryNo)
                .stream().map(Equipment::getEquipNo).collect(Collectors.toList());

        Page<CopperParticleCheckRecord> list = PageHelper.startPage(page, size)
                .doSelectPage(() -> copperParticleMapper.listHistoryRecord(deviceNo, startDate, endDate));
        PagedResult pagedResult = new PagedResult();
        pagedResult.setPage(list.getPageNum());
        pagedResult.setTotal(list.getPages());
        pagedResult.setRecords(list.getTotal());
        pagedResult.setRows(list.getResult());
        return pagedResult;
    }

    /**
     * @description: 获取铜粒子数据长度
     * @param deviceNo,isOk,
     */
    @Override
    public long getAllRecordsFromCacheLength(String deviceNo, IsOk isOk) throws Exception {
        return getAllRecordsFromCacheLength(deviceNo,isOk,"","");
    }

    @Override
    public boolean recordIsOk(CopperParticleCheckRecord record){
        return "OK".equals(record.getaIsOk())&&"OK".equals(record.getbIsOk())?true:false;
    }

    @Override
    public void persistDataOfYesterday(Date date) {
        //生成key
        String key =generateDefaultCacheKey(date,RedisType.HASH);
        List<CopperParticleCheckRecord> records = null;
        //遍历Hash，完成检测记录的持久化
        records = hashCacheService.hScan(key,"*",CopperParticleCheckRecord.class);
        //不停地取，持久到数据库，直到取完数据
        for(CopperParticleCheckRecord record:records){
            try {
                this.insertRecord(record);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        /*//删除缓存中已持久化的数据
        ((AbstractRedisCacheService)hashCacheService).delete(key);
        //((AbstractRedisCacheService)hashCacheService).delete(key.replace(RedisType.HASH.getCode(),RedisType.ZSET.getCode()));

        //删除阴极板编号索引
        String indexKey = new CacheKeyBuilder()
                .addSecondaryKey(ModuleName.COPPER_PARTICLE_RECORDS.getCode()+new SimpleDateFormat("yyyyMMdd").format(new Date()))
                .addSecondaryKey(RedisType.INDEX.getCode())
                .addSecondaryKey("productNo")
                .build();
        ((AbstractRedisCacheService)hashCacheService).delete(key);*/

        //删除所有date日的缓存数据
        Set<String> keys = ((AbstractRedisCacheService)hashCacheService).keys(generateDefaultCacheKey(date,RedisType.NULL)+"*");
        for (String k:keys){
            ((AbstractRedisCacheService)hashCacheService).delete(k);
        }
    }

    @Override
    public void addIndexOfProductNo(String productNo, String field) throws Exception {
        String key = new CacheKeyBuilder()
                .addSecondaryKey(ModuleName.COPPER_PARTICLE_RECORDS.getCode()+new SimpleDateFormat("yyyyMMdd").format(new Date()))
                .addSecondaryKey(RedisType.INDEX.getCode())
                .addSecondaryKey("productNo")
                .build();
        try {
            hashCacheService.hSet(key,productNo,field);
        } catch (CacheException e) {
            throw new Exception("添加缓存索引失败:"+e.getMessage(),e);
        }
    }

    @Override
    public CopperParticleCheckRecord getRecordFromCache(String productNo) {
        String key = new CacheKeyBuilder()
                .addSecondaryKey(ModuleName.COPPER_PARTICLE_RECORDS.getCode()+new SimpleDateFormat("yyyyMMdd").format(new Date()))
                .addSecondaryKey(RedisType.INDEX.getCode())
                .addSecondaryKey("productNo")
                .build();

        //获取索引键
        String result = hashCacheService.hGet(key,productNo,String.class);
        String key2 = generateDefaultCacheKey(new Date(),RedisType.HASH);
        //获取记录并转化
        CopperParticleCheckRecord record = hashCacheService.hGet(key2,result,CopperParticleCheckRecord.class);

        return record;
    }

    @Override
    public List<CopperParticleCheckRecord> getCopperParticleRecords(String deviceNo, Date startTime, Date endTime, IsOk isOk, String productNo, String particleType) {
        return copperParticleMapper.collectCopperParticleRecords(deviceNo,startTime,endTime,isOk!=null?isOk.getCode():null,productNo,particleType);
    }


    @Override
    public List<CopperParticleCheckRecord> getCopperParticleRecords(String deviceNo, Date startTime, Date endTime, IsOk isOk) {
        return this.getCopperParticleRecords(deviceNo,startTime,endTime,isOk);
    }

    @Override
    public List<CopperParticleCheckRecord> getCopperParticleRecords(String deviceNo, Date startTime, Date endTime) {
        return this.getCopperParticleRecords(deviceNo,startTime,endTime);
    }

    @Override
    public PagedResult getCopperParticleRecords(String deviceNo, Date startTime, Date endTime, IsOk isOk, String productNo, String particleType, int page, int pageSize) {
        PageHelper.startPage(page,pageSize);
        List<CopperParticleCheckRecord> list = this.getCopperParticleRecords(deviceNo,startTime,endTime,isOk,productNo,particleType);
        PageInfo<CopperParticleCheckRecord> pageList = new PageInfo<>(list);
        PagedResult pagedResult = new PagedResult();
        pagedResult.setPage(page);
        pagedResult.setTotal(pageList.getPages());
        pagedResult.setRows(list);
        pagedResult.setRecords(pageList.getTotal());
        return pagedResult;
    }


    @Override
    public PagedResult getCopperParticleRecords(String deviceNo, Date startTime, Date endTime, IsOk isOk, int page, int pageSize) {
        return this.getCopperParticleRecords(deviceNo,startTime,endTime,isOk,page,pageSize);
    }

    @Override
    public PagedResult getCopperParticleRecords(String deviceNo, Date startTime, Date endTime, int page, int pageSize) {
        return this.getCopperParticleRecords(deviceNo,startTime,endTime,page,pageSize);
    }

    @Override
    public PagedResult getCopperParticleRecordsOfToday(String deviceNo, IsOk isOk, String productNo, String particleType, int page, int pageSize) {
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.HOUR_OF_DAY,0);
        calendar.set(Calendar.MINUTE,0);
        calendar.set(Calendar.SECOND,0);

        Date startTime = calendar.getTime();
        Date endTime = new Date();


        return this.getCopperParticleRecords(deviceNo,startTime,endTime,isOk,productNo,particleType,page,pageSize);
    }

    @Override
    public List<CopperParticleCheckRecord> getCopperParticleRecordsOfToday(String deviceNo, IsOk isOk, String productNo, String particleType) {
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.HOUR_OF_DAY,0);
        calendar.set(Calendar.MINUTE,0);
        calendar.set(Calendar.SECOND,0);

        Date startTime = calendar.getTime();
        Date endTime = new Date();

        return this.getCopperParticleRecords(deviceNo,startTime,endTime,isOk,productNo,particleType);
    }

    @Override
    public void updateProductNoOfCopperParticleCheckRecord(Long recordId, String newProductNo) throws Exception {
        //合法性判断
        if (recordId == null || StringUtils.isEmpty(newProductNo)){
            throw new Exception("记录ID或阴极板编号不能为空");
        }

        //更新阴极板编号
        copperParticleMapper.updateProductNoOfRecord(recordId,newProductNo);
    }

    /**
     * 模糊匹配，key是否符合pattern
     * @param pattern
     * @param key
     * @return
     */
    private boolean patternMatch(String pattern,String key){
        String temp = pattern.replaceAll("\\*","([a-zA-Z0-9]*)");
        return Pattern.compile(temp).matcher(key).find();
    }

    /**
     * 生成默认的key值或是模式匹配串
     * @param date
     * @param type 为空时替换为'*'
     * @return
     */
    private String generateDefaultCacheKey(@NotNull Date date,RedisType type){
        return new CacheKeyBuilder()
                .addSecondaryKey(ModuleName.COPPER_PARTICLE_RECORDS.getCode()+new SimpleDateFormat("yyyyMMdd").format(date))
                .addSecondaryKey(type==null?"*":type.getCode())
                .build();
    }

    /**
     * 生成默认的Hash field值或是模式匹配串值
     * @param id 为空时替换为'*'
     * @param isOk 为空时替换为'*'
     * @param productCode 为空时替换为'*'
     * @param particleType 为空时替换为'*'
     * @return
     */
    private String generateDefaultHashField(String id,String deviceNo,IsOk isOk,String productCode,String particleType){
        return new CacheKeyBuilder("")
                .addSecondaryKey(StringUtils.isEmpty(id)?"*":id)
                .addSecondaryKey(StringUtils.isEmpty(deviceNo)?"*":deviceNo)
                .addSecondaryKey(isOk==null?"*":isOk.getCode())
                .addSecondaryKey(StringUtils.isEmpty(productCode)?"*":productCode)
                .addSecondaryKey(StringUtils.isEmpty(particleType)?"*":particleType)
                .build();
    }



    /**
     * 将记录插入到符合条件的临时表中
     * @param record
     * @throws CacheException
     */
    private void insertToTempleTables(CopperParticleCheckRecord record) throws CacheException {
        String option1 = generateDefaultCacheKey(new Date(),RedisType.TEMP);//生成临时表前缀名
        String option2 = generateDefaultHashField(record.getId().toString(),record.getDeviceNo(),recordIsOk(record)?IsOk.OK:IsOk.NG,record.getProductCode(),record.getType());//生成临时表后缀名
        String option = option1+":"+option2;//拼接临时表名
        Set<String> keys = zSetCacheService.keys(generateDefaultCacheKey(new Date(),RedisType.TEMP)+"*");//获取所有临时表名
        for(String key:keys){
            //进行条件匹配，符合条件的临时表会被插入新值
            if(patternMatch(key,option)){
                zSetCacheService.zAdd(key,option2,record.getDatetime().getTime());
            }
        }
    }

    /**
     * 根据field从Hash中取出对应检测记录
     * @param fields
     * @return
     */
    private List<CopperParticleCheckRecord> getRecordsByFields(Set<String> fields){
        List<CopperParticleCheckRecord> result = new ArrayList<>();
        //根据field从Hash中取出对应检测记录
        /*for(String field:fields){
            result.add(hashCacheService.hGet(generateDefaultCacheKey(new Date(),RedisType.HASH),field,CopperParticleCheckRecord.class));

        }*/
        result = hashCacheService.hMGet(generateDefaultCacheKey(new Date(),RedisType.HASH),new ArrayList<>(fields),CopperParticleCheckRecord.class);
        return result;
    }


}
